/*
 * @Author: Jiraiya
 * @Date: 2020-03-03 21:20:17
 * @LastEditors: Jiraiya
 * @LastEditTime: 2020-03-10 17:04:36
 * @Description: 
 */

// proxy在语言层面做了一层拦截，外界对该对象访问都必须先通过这层拦截

//🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏
//get(target, propKey, proxy)

//数组负数获取值
function createArray(){
    const handler = {
        get(target, propKey){
            propKey = Number(propKey)
            if(typeof propKey === 'number'){
                if(propKey < 0){
                    return target[target.length + propKey]
                }else{
                    return target[propKey]
                }
            }else{
                throw new Error('arguments must is a number')
            }
        }
    }

    return new Proxy([...arguments], handler)
}

const arr = createArray('a', 'b', 'c')
console.log(arr[0], arr[1], arr[-1])// a b c

// 对象属性的链式操作
function pipe(val, obj){
    const arr = []
    const proxy = new Proxy({}, {
        get(target, fnName){
            if(fnName === 'get'){
                return arr.reduce((val, fn)=>{
                    return fn(val)
                }, val)
            }else{
                arr.push(obj[fnName])
                return proxy
            }
        }
    })
    return proxy;
}

console.log(pipe(3, {
    double: n => n*2,
    pow: n => n**2
}).double.pow.get) //36

//🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏
//set(target, props, val, proxy)

// 对某个对象的属性值做限制
const obj = {}
const proxy = new Proxy(obj, {
    set(target, prop, val){
        console.log(prop,val)
        if(prop === 'age'){
            if(val > 0 && val < 100){
                target[prop] = val
            }else{
                throw new Error('age must range 1 from 99')
            }
        }else{
            target[prop] = val
        }
    }
})
proxy.age = 100

//创建私有变量


//🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏
//apply(target, that, args) 拦截函数调用，call和apply
function f(){ return 'slef' }
const proxy = new Proxy(f, {
    apply(target, that, args){
        return {that, args}
    }
}) 

console.log(proxy(1, 2, 3))
// { that: undefined, args: [ 1, 2, 3 ] }
console.log(proxy.apply({a: 1}, [1]))
// { that: { a: 1 }, args: [ 1 ] }


//🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏
// has(target, key) in操作符  对不可扩展和不可配置的拦截报错  for..in拦截失效

const obj = {a: 1, _b:2}
Object.setPrototypeOf(obj, {c: 3})
const proxy = new Proxy(obj, {
    has(target, key){
        if(key.startsWith('_')){
            return false;
            throw new Error('key is a private value')
        }else{
            return key in target;
        }
    }
})

console.log('_b' in proxy) // false
console.log('a' in proxy) // true
console.log('c' in proxy) // true

//🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏
// construct(target, args, newTarget)拦截new操作符 返回必须是一个对象

const p = new Proxy(function f(){}, {
    construct(target, args, newTarget){
        console.log(target, args, newTarget)
        return {a: 1}
    }
})
const b = new p(1, 2)
// [Function: f] [ 1, 2 ] [Function: f]

//🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏
// deleteProperty(target, key) 拦截delete 不可配置的属性拦截报错

const p = new Proxy({a:1, b:2}, {
    deleteProperty(target, key){
        return delete target[key]
    }
})
console.log(delete p.a) // true
console.log(delete p.c) // true

//🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏

// defineProperty(target, key, desc) 如果目标对象不可扩展（non-extensible），
// 则defineProperty不能增加目标对象上不存在的属性，否则会报错。另外，如果目标对象
// 的某个属性不可写（writable）或不可配置（configurable），则defineProperty方法
// 不得改变这两个设置。

const p = new Proxy({}, {
    defineProperty(target, key, desc){
        console.log(desc)
    }
})

Object.defineProperty(p, 'a', {value: 1})
console.log(p.a)

//🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏
// getOwnPropertyDescriptor(target, key)
const p = new Proxy({a: 1, _b:2}, {
    getOwnPropertyDescriptor(target, key){
        return key.startsWith('_') ? undefined 
        : Object.getOwnPropertyDescriptor(target, key);
    }
})
console.log(Object.getOwnPropertyDescriptor(p, 2))// undefined

//🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏
// getPrototypeOf(target) 拦截 Object.getPrototypeOf _proto isPrototypeOf instanceof
// getPrototypeOf方法的返回值必须是对象或者null，否则报错。另外，如果目标对象
// 不可扩展（non-extensible）， getPrototypeOf方法必须返回目标对象的原型对象。

//🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏
// isExtensible方法拦截Object.isExtensible操作。
// 注意，该方法只能返回布尔值，否则返回值会被自动转为布尔值。
// 这个方法有一个强限制，它的返回值必须与目标对象的isExtensible属性保持一致，否则就会抛出错误。

//🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏
// ownKeys方法用来拦截对象自身属性的读取操作。具体来说，拦截以下操作。

// Object.getOwnPropertyNames()
// Object.getOwnPropertySymbols()
// Object.keys()
// for...in循环

// ownKeys方法用来拦截对象自身属性的读取操作。具体来说，拦截以下操作。

// Object.getOwnPropertyNames()
// Object.getOwnPropertySymbols()
// Object.keys()
// for...in循环


class Observe{
    constructor(){
        this._collections = new Map()
    }

    _proxy(obj){
        const self = this;
        return new Proxy(obj, {
            set(target, key, value){
                if(Reflect.get(obj, key) !== value){
                    Reflect.set(obj, key, value)
                    const currentArr = self._collections.get(obj) || [];
                    currentArr.forEach(e => {
                        Reflect.apply(e, self, [])
                    })
                }
            }
        })
    }
    
    add(obj, fn){
        const { _collections } = this;
        if(_collections.has(obj)){
            _collections.set(obj, [..._collections.get(obj), fn])
        }else{
            _collections.set(obj, [fn])
        }
        return this._proxy(obj)
    }


}

const o = new Observe()
const obj = {}
const p1 = o.add(obj, ()=>{
    console.log(111)
})
o.add(obj, ()=>{
    console.log(333)
})
const p2 = o.add({}, ()=>{
    console.log(222)
})

p1.a = 1
p1.a = 2
p2.b = 3
//111
//333
//111
//333
//222